package com.xquant.pricing.calc.service.impl.otcOption;

import cn.hutool.core.util.NumberUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.xquant.common.core.utils.DateUtils;
import com.xquant.common.engine.xPP.cxf.XQuantPPService;
import com.xquant.common.engine.xPP.cxf.XQuantPPStub;
import com.xquant.common.engine.xPP.cxf.webservice.*;
import com.xquant.common.engine.xPP.request.*;
import com.xquant.pricing.calc.entity.RequestCodeEnum;
import com.xquant.pricing.pricingConfig.mapper.OptionContractMapper;
import com.xquant.pricing.pricingConfig.mapper.TargetParamMapper;
import com.xquant.pricing.utils.CacheUtil;
import com.xquant.system.api.RemoteDailyPriceService;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.UnsupportedEncodingException;
import java.util.*;

@Component
public class CalcMarketServiceImpl4OtcOption extends CalcCommonService4OtcOption {
    @Autowired
    private OptionContractMapper contractMapper;
    @Autowired
    private TargetParamMapper targetParamMapper;
    
    @Autowired
    protected XQuantPPService ppService;

    @Autowired
    private RemoteDailyPriceService remoteDailyPriceService;

    private static final Logger logger = LoggerFactory.getLogger(CalcMarketServiceImpl4OtcOption.class);

    List<String> underlyingList = new ArrayList<>();

    @Override
    public String getSupportedRequestCode() {
        return RequestCodeEnum.CALC_MARKET.getCode();
    }

    @Override
    public CalcEngineParam getCalcPricingParamByCfgId(CalcEngineParam calcEngineParam, String cfgId) {
        List<Map> underlyingParam = new ArrayList<>();
        Instrument instrument = calcEngineParam.getInstrument();
        CalcEngineParam ret = new CalcEngineParam();
        ret.setCalcPricingParam(new CalcPricingParam());
        // 合约定价参数
        List<String> underlyIds = getUnderlyIds(instrument);
        Map<String, Object> contractParam = getContractCfg(instrument, cfgId, underlyIds);
        ret.getCalcPricingParam().setDiscount(new Ir());
        ret.getCalcPricingParam().setCalcPPCode(MapUtils.getString(contractParam, "DLL_NAME"));
        ret.getCalcPricingParam().getDiscount().setValue(
                MapUtils.getString(contractParam, "DIS_TYPE", ""),
                MapUtils.getString(contractParam, "DIS_VALUE", ""),
                MapUtils.getString(contractParam, "DIS_DAYCOUNT", "")
        );
        String diyParam = MapUtils.getString(contractParam, "DIY_PARAM");
        try {
            if (diyParam != null) {
                JSONObject customPricingParam = JSON.parseObject(diyParam);
                ret.getCalcPricingParam().setCustomPricingParam(customPricingParam.get("customPricingParam"));
            }
        } catch (Exception ex) {

        }
        List<String> listUnderlying = new ArrayList<>();
        // 标的定价参数
        Map<String, Object> param = new WeakHashMap<>();
        param.put("cfgId", cfgId);

        int underlySize = underlyIds.size();
        if (underlySize == 0) {
            return ret;
        }
        List<Map> underlyingIdLst = new ArrayList<>();
        String underlying = new String();
        for (int i = 0; i < underlySize; i++) {
            underlyingIdLst.add(new HashMap());
            String[] item = underlyIds.get(i).split(",");
            underlyingIdLst.get(i).put("uICode", item[0]);
            underlyingIdLst.get(i).put("uAType", item[1]);
            underlyingIdLst.get(i).put("uMType", item[2]);
            underlying = underlyIds.get(i);
        }
        param.put("underlyingIds", underlyingIdLst);
        param.put("iCode", instrument.getId());
        if (CacheUtil.getInstance().getCacheData("listUnderlying") != null) {
            listUnderlying = (List<String>) CacheUtil.getInstance().getCacheData("listUnderlying");
        }
        if (listUnderlying.contains(underlying)) {
            underlyingParam = (List<Map>) CacheUtil.getInstance().getCacheData(cfgId + underlying);
            if (underlyingParam == null) {
                underlyingParam = new ArrayList<>();
            }
        } else {
            underlyingParam = targetParamMapper.queryUnderlyingCfg(param);
            CacheUtil.getInstance().addCacheData(cfgId + underlying, underlyingParam);
            listUnderlying.add(underlying);
            CacheUtil.getInstance().addCacheData("listUnderlying", listUnderlying);
        }
        //System.out.println(underlying);
        ret.getCalcPricingParam().setUnderlyingPricingParams(new ArrayList<>());
        for (int i = 0; i < underlyingParam.size(); i++) {
            ret.getCalcPricingParam().getUnderlyingPricingParams().add(new UnderlyingPricingParam());
            ret.getCalcPricingParam().getUnderlyingPricingParams().get(i).setUnderlyingID(underlyingParam.get(i).get("U_I_CODE")
                    + "," + underlyingParam.get(i).get("U_A_TYPE")
                    + "," + underlyingParam.get(i).get("U_M_TYPE"));
            // 红利率
            ret.getCalcPricingParam().getUnderlyingPricingParams().get(i).setDividend(new Ir());
            ret.getCalcPricingParam().getUnderlyingPricingParams().get(i).getDividend().setValue(
                    MapUtils.getString(underlyingParam.get(i), "DIV_TYPE", ""),
                    MapUtils.getString(underlyingParam.get(i), "DIV_VALUE", ""),
                    MapUtils.getString(underlyingParam.get(i), "DIV_DAYCOUNT", ""));
            // 融资率
            ret.getCalcPricingParam().getUnderlyingPricingParams().get(i).setFunding(new Ir());
            ret.getCalcPricingParam().getUnderlyingPricingParams().get(i).getFunding().setValue(
                    MapUtils.getString(underlyingParam.get(i), "FUND_TYPE", ""),
                    MapUtils.getString(underlyingParam.get(i), "FUND_VALUE", ""),
                    MapUtils.getString(underlyingParam.get(i), "FUND_DAYCOUNT", ""));
            // 融资率 用贴现替代
            ret.getCalcPricingParam().getUnderlyingPricingParams().get(i).setFunding(new Ir());
            ret.getCalcPricingParam().getUnderlyingPricingParams().get(i).getFunding().setValue(
                    MapUtils.getString(contractParam, "DIS_TYPE", ""),
                    MapUtils.getString(contractParam, "DIS_VALUE", ""),
                    MapUtils.getString(contractParam, "DIS_DAYCOUNT", ""));
            // 波动率
            ret.getCalcPricingParam().getUnderlyingPricingParams().get(i).setVolSurface(new Ir());
            ret.getCalcPricingParam().getUnderlyingPricingParams().get(i).getVolSurface().setValue(
                    MapUtils.getString(underlyingParam.get(i), "VOL_TYPE", ""),
                    MapUtils.getString(underlyingParam.get(i), "VOL_VALUE", ""),
                    MapUtils.getString(underlyingParam.get(i), "VOL_DAYCOUNT", ""));
            //远期
            ret.getCalcPricingParam().getUnderlyingPricingParams().get(i).setForward(new Ir());
            ret.getCalcPricingParam().getUnderlyingPricingParams().get(i).getForward().setValue(
                    MapUtils.getString(underlyingParam.get(i), "FWD_TYPE", ""),
                    MapUtils.getString(underlyingParam.get(i), "FWD_VALUE", ""),
                    MapUtils.getString(underlyingParam.get(i), "FWD_DAYCOUNT", "")
            );

            ret.getCalcPricingParam().getUnderlyingPricingParams().get(i).setSpotQuoteType(MapUtils.getString(underlyingParam.get(i), "SPOT_QUOTE_TYPE"));
        }

        return ret;
    }

    /**
     * @param query
     * 获取 波动率曲面插值
     * 计算日 valueDate
     * 到期日 InterpolationDate
     * 曲面代码 code
     * 定盘价格 spot
     * 行权价 strike
     * @return
     */
    public CalcVolSurfaceRateBase64Response calcVolSurfaceRateBase64(Map<String, Object> query)  {
        CalcVolSurfaceRateBase64 calcVolSurfaceRateBase64 = new CalcVolSurfaceRateBase64();
        CalcVolSurfaceRateParam calcVolSurfaceRateParam = new CalcVolSurfaceRateParam();
        calcVolSurfaceRateParam.setValueDate(String.valueOf(query.get("valueDate")));
        calcVolSurfaceRateParam.setInterpolationDate(String.valueOf(query.get("InterpolationDate")));
        try {
            calcVolSurfaceRateParam.setCode(Base64.getEncoder().encodeToString(String.valueOf(query.get("code")).getBytes("GBK")));
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
        calcVolSurfaceRateParam.setSpot(String.valueOf(query.get("spot")));
        calcVolSurfaceRateParam.setStrike(String.valueOf(query.get("strike")));
        XQuantPPStub stub = null;
        calcVolSurfaceRateBase64.setParam(calcVolSurfaceRateParam);
        try {
            stub = ppService.borrowStubServer();
            CalcVolSurfaceRateBase64Response calcVolSurfaceRateBase64Response = stub.calcVolSurfaceRateBase64(calcVolSurfaceRateBase64);
            logger.info(JSON.toJSONString(calcVolSurfaceRateBase64Response.getResult()));
            return calcVolSurfaceRateBase64Response;
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            if (stub != null) {
                ppService.returnStub(stub);
            }
        }

    }

    /**
     * 获取 分红曲线插值
     * 计算日 valueDate
     * 曲线代码  code
     * 到期日 PeriodsOrDates
     * @param query
     * @return
     */
    public CalcDivCurveRatesBase64Response CalcDivCurveRateBase64(Map<String, Object> query)  {

        CalcDivCurveRatesBase64 calcDivCurveRatesBase64 = new CalcDivCurveRatesBase64();
        CalcDivCurveRatesParam calcDivCurveRatesParam = new CalcDivCurveRatesParam();
        calcDivCurveRatesParam.setValueDate(String.valueOf(query.get("valueDate")));
        try {
            calcDivCurveRatesParam.setDivCurveCode(Base64.getEncoder().encodeToString(String.valueOf(query.get("code")).getBytes("GBK")));
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
        calcDivCurveRatesParam.setPeriodsOrDates(new ArrayOfString().setString(Arrays.asList(String.valueOf(query.get("PeriodsOrDates")))));

        XQuantPPStub stub = null;
        calcDivCurveRatesBase64.setParam(calcDivCurveRatesParam);
        try {
            stub = ppService.borrowStubServer();
            CalcDivCurveRatesBase64Response calcDivCurveRatesBase64Response = stub.calcDivCurveRatesBase64(calcDivCurveRatesBase64);
            logger.info(JSON.toJSONString(calcDivCurveRatesBase64Response.getResult()));
            return calcDivCurveRatesBase64Response;
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            if (stub != null) {
                ppService.returnStub(stub);
            }
        }
    }


    /**
     * 获取 贴现曲线插值
     * 计算日  valueDate
     * 到期日  PeriodsOrDates
     * 曲线代码 code
     * @param query
     * @return
     */
    public CalcCurveRatesBase64Response calcCurveRateBase64(Map<String, Object> query) {
        CalcCurveRatesBase64 calcCurveRatesBase64 = new CalcCurveRatesBase64();
        CalcCurveRatesParam calcCurveRatesParam = new CalcCurveRatesParam();
        calcCurveRatesParam.setValueDate(String.valueOf(query.get("valueDate")));
        try {
            calcCurveRatesParam.setCurveCode(Base64.getEncoder().encodeToString(String.valueOf(query.get("code")).getBytes("GBK")));
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
        calcCurveRatesParam.setPeriodsOrDates(new ArrayOfString().setString(Arrays.asList(String.valueOf(query.get("PeriodsOrDates")))));
        calcCurveRatesParam.setIsPeriod(false);

        XQuantPPStub stub = null;
        calcCurveRatesBase64.setParam(calcCurveRatesParam);
        try {
            stub = ppService.borrowStubServer();
            CalcCurveRatesBase64Response calcCurveRatesBase64Response = stub.calcCurveRateBase64(calcCurveRatesBase64);
            logger.info(JSON.toJSONString(calcCurveRatesBase64Response.getResult()));

            return calcCurveRatesBase64Response;
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            if (stub != null) {
                ppService.returnStub(stub);
            }
        }

    }

    /**
     * 获取定价参数，若为曲线进行插值
     *
     * @param calcEngineParam
     * @param cfgId
     * @return
     */
    public CalcEngineParam getCalcPricingParamByCfgIdWarpperWithInterpolation(CalcEngineParam calcEngineParam, String cfgId) {

        Instrument instrument = calcEngineParam.getInstrument();
        String productType = instrument.getProductType();
        String initProductType = instrument.getInitProductType();

        JSONObject termsheet = instrument.getTermsheet();
        String effectiveDate = String.valueOf(instrument.getTermsheet().get("effectiveDate"));
        String terminationDate = String.valueOf(termsheet.get("terminationDate"));
        String strikePercentage = String.valueOf(termsheet.get("strikePercentage"));
        String strike = String.valueOf(termsheet.get("strike"));
        CalcEngineParam sourceCalcPricingParam = getCalcPricingParamByCfgId(calcEngineParam, cfgId);
        CalcPricingParam calcPricingParam = sourceCalcPricingParam.getCalcPricingParam();
        List<UnderlyingPricingParam> underlyingPricingParams = calcPricingParam.getUnderlyingPricingParams();
        if (null==underlyingPricingParams||underlyingPricingParams.isEmpty()){
            return sourceCalcPricingParam;
        }
        /*计算日*/
        String valueDate = calcEngineParam.getCalcPricingParam().getValueDate();
        String UnderlyingID = underlyingPricingParams.get(0).getUnderlyingID();
        /*获取最新定盘价格*/
        Map<String, Object> dailyMap = new HashMap<>();
        String[] split = UnderlyingID.split(",");
        dailyMap.put("iCode", split[0]);
        dailyMap.put("aType", split[1]);
        dailyMap.put("mType", split[2]);
        dailyMap.put("begDate", valueDate);
        /*逻辑待定*/
        /*// 计算日在期权开始日之前取计算日当天行情   ，否则就是期权开始日当天得行情
        if (LocalDate.parse(valueDate).isAfter(LocalDate.parse(effectiveDate))) {
            dailyMap.put("begDate", valueDate);

        }else {
            dailyMap.put("begDate", effectiveDate);

        }*/
        //调用市场数据的dailyPrice方法
        Map dailyResultMap = remoteDailyPriceService.getDailyPriceService(dailyMap);
        if (dailyResultMap == null || dailyResultMap.get("error") != null) {
        }else {
        //            P013XEQ-2115
        //账户/组合监控界面应用实时行情
            if (valueDate.equals(DateUtils.getDate())&&(dailyResultMap.get("dpRelTime") != null) && String.valueOf(dailyResultMap.get("dpRelTime")).length() > 0){
                sourceCalcPricingParam.getCalcPricingParam().getUnderlyingPricingParams().get(0).setSpotPrice(NumberUtil.roundStr(String.valueOf(dailyResultMap.get("dpRelTime")),2));
            }else {
                sourceCalcPricingParam.getCalcPricingParam().getUnderlyingPricingParams().get(0).setSpotPrice(NumberUtil.roundStr(String.valueOf(dailyResultMap.get("dpClose")),2));
            }
        }
        /*只处理区间累计和三层阶梯*/
        if ("rangeaccrual".equals(String.valueOf(productType).toLowerCase(Locale.ROOT))){

        }
        else if ("multilayerdigital".equals(String.valueOf(productType).toLowerCase(Locale.ROOT))
            &&"multidigital".equals(String.valueOf(initProductType).toLowerCase(Locale.ROOT))
        ){

        }else {
            return sourceCalcPricingParam;
        }
        Ir discount = calcPricingParam.getDiscount();
        Ir dividend = underlyingPricingParams.get(0).getDividend();
        Ir volSurface = underlyingPricingParams.get(0).getVolSurface();
        String spotPrice = underlyingPricingParams.get(0).getSpotPrice();

        if (null!=discount) {
            //贴现曲线处理
            String discountCode = discount.getCode();
            if (StringUtils.isNotEmpty(discountCode)){
                //插值
                Map query = new HashMap<>();
                query.put("valueDate",valueDate);
                query.put("code",discountCode);
                query.put("PeriodsOrDates",terminationDate);
                HashMap hashMap = contractMapper.queryTircurveDaycounter(query);
                String daycounter = String.valueOf(hashMap.getOrDefault("daycounter", ""));
                CalcCurveRatesBase64Response calcCurveRatesBase64Response = this.calcCurveRateBase64(query);
                String aDouble = String.valueOf(calcCurveRatesBase64Response.getResult().getZeroRates().getDouble().get(0));
                discount = new Ir();
                discount.setRate(aDouble);
                discount.setDayCount(daycounter);
            }
        }
        //分红处理
        if (null!=dividend){
            String dividendCode = dividend.getCode();
            if (StringUtils.isNotEmpty(dividendCode)) {
                //插值
                Map query = new HashMap<>();
                query.put("valueDate",valueDate);
                query.put("code",dividendCode);
                query.put("PeriodsOrDates",terminationDate);
                HashMap hashMap = contractMapper.queryTdivyieldDaycounter(query);
                String daycounter = String.valueOf(hashMap.getOrDefault("daycounter", ""));
                CalcDivCurveRatesBase64Response calcDivCurveRatesBase64Response = this.CalcDivCurveRateBase64(query);
                String aDouble = String.valueOf(calcDivCurveRatesBase64Response.getResult().getZeroRates().getDouble().get(0));
                dividend = new Ir();
                dividend.setRate(aDouble);
                dividend.setDayCount(daycounter);
            }
        }

        //波动率处理
        if (null!=volSurface){
            String volSurfaceCode = volSurface.getCode();
            if (StringUtils.isNotEmpty(volSurfaceCode)) {
                //插值
                Map query = new HashMap<>();
                query.put("valueDate",valueDate);
                query.put("code",volSurfaceCode);
                query.put("InterpolationDate",terminationDate);
                query.put("strike",spotPrice);
                query.put("spot",spotPrice);
                HashMap hashMap = contractMapper.queryTvolsurfaceDaycounter(query);
                String daycounter = String.valueOf(hashMap.getOrDefault("daycounter", ""));
                CalcVolSurfaceRateBase64Response calcVolSurfaceRateBase64Response = this.calcVolSurfaceRateBase64(query);
                String volSurfaceRate = String.valueOf(calcVolSurfaceRateBase64Response.getResult());
                volSurface = new Ir();
                volSurface.setRate(volSurfaceRate);
                volSurface.setDayCount(daycounter);
            }
        }
        logger.info(JSON.toJSONString(discount));
        logger.info(JSON.toJSONString(dividend));
        logger.info(JSON.toJSONString(volSurface));
        sourceCalcPricingParam.getCalcPricingParam().setDiscount(discount);
        sourceCalcPricingParam.getCalcPricingParam().getUnderlyingPricingParams().get(0).setFunding(discount);
        sourceCalcPricingParam.getCalcPricingParam().getUnderlyingPricingParams().get(0).setVolSurface(volSurface);
        sourceCalcPricingParam.getCalcPricingParam().getUnderlyingPricingParams().get(0).setDividend(dividend);
        return sourceCalcPricingParam;
    }
}
